home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 05 - 1989 / 05.12 Dec 89 / Chat Project / Chat.c next >
Encoding:
C/C++ Source or Header  |  1989-08-22  |  21.8 KB  |  823 lines  |  [TEXT/KAHL]

  1. /* ======== Chatterbox ======= */
  2.  
  3. #include    <AppleTalk.h>
  4.  
  5. /*** defines */
  6. #define        NULL                0L
  7. #define        MAXUSERS        64
  8. #define        BUFSIZE        600
  9. #define        NONE                0
  10. #define        REGISTER        1
  11. #define        WAITING        2
  12. #define        SCANNING        3
  13. #define        SENDING        4
  14. #define        RECEIVING        5
  15. #define        MAXTEXT        32000L    
  16. #define        MAXMSG        50
  17. #define        DISPLINES        8
  18. #define        RETURNKEY    '\015'
  19. #define        ENTERKEY        '\003'
  20.  
  21. /*** data about a single user */
  22. typedef struct UserRecord {
  23.     AddrBlock    addr;
  24.     int            found;
  25.     } UserRecord;
  26.  
  27. /*** Globals */
  28. int                        DoneFlag;                        /* true when done */
  29. WindowPtr                TheWindow;                    /* main window ptr */
  30. ControlHandle            ScanButton;                    /* button to scan user list */
  31. ControlHandle            SelectAllButton;                /* select all button */
  32. ListHandle                UserList;                        /* list of users currently on the net */
  33. TEHandle                MyMessage;                    /* message I've typed in */
  34. TEHandle                InMessages;                    /* messages I've recieved */
  35. MenuHandle            AppleMenu;                    /* apple menu */
  36. MenuHandle            FileMenu;                        /* file menu */
  37. MenuHandle            EditMenu;                        /* edit menu */
  38. MenuHandle            OptionMenu;                    /* options menu */
  39. DDPProto                **InABRecord;                /* holds incomming messages */
  40. UserRecord            OtherUsers[MAXUSERS];    /* addresses of other users */
  41. EntityName            MyEntityName;                /* name this process is registered under */
  42. Ptr                        MyNameBuf;                    /* name buffer for this station (used by NBP) */
  43. Str255                    MyName;                        /* name the user wants to go by */
  44. int                        MySocketID;                    /* socket id of this user */
  45. int                        NumOtherUsers;                /* number of other users found on the net */
  46. int                        TheError;                        /* for error handling */
  47. int                        EchoFlag;                        /* true if echo-back is turned on */
  48. int                        BeepFlag;                        /* true if beep on receipt turned on */
  49. int                        SelectFlag;                        /* true if auto select new users turned on */
  50. char                        RecBuffer[BUFSIZE];        /* buffer for reecieved information */
  51. Rect                        StatusR;                            /* rectangle for status info */
  52. Rect                        DragRect;                        /* used by dragwindow */
  53.  
  54. /*||||||||||||||||||||*/
  55. main()
  56.     {
  57.     EventRecord        event;
  58.     WindowPtr            whichWindow;
  59.     char                    key;
  60.  
  61.     Initialize ();
  62.     while ( !DoneFlag ) {
  63.  
  64.         GetNextEvent ( everyEvent, &event );
  65.         switch ( event.what ) {
  66.             case activateEvt:
  67.                 whichWindow = (WindowPtr) event.message;
  68.                 if ( whichWindow == TheWindow ) {
  69.                     if ( event.modifiers & activeFlag ) {
  70.                         TEActivate ( MyMessage );
  71.                         LActivate ( true, UserList );
  72.                         ShowControl ( ScanButton );
  73.                         ShowControl ( SelectAllButton );
  74.                         }
  75.                     else {
  76.                         TEDeactivate ( MyMessage );
  77.                         LActivate ( false, UserList );
  78.                         HideControl ( ScanButton );
  79.                         HideControl ( SelectAllButton );
  80.                         }
  81.                     }
  82.                 break;
  83.             case updateEvt:
  84.                 whichWindow = (WindowPtr) event.message;
  85.                 if ( whichWindow == TheWindow ) {
  86.                     BeginUpdate ( TheWindow );
  87.                     DoUpdate ( );
  88.                     EndUpdate ( TheWindow );
  89.                     }
  90.                 break;
  91.             case mouseDown:
  92.                 switch ( FindWindow ( event.where, &whichWindow ) ) {
  93.                     case inMenuBar:
  94.                         DoMenu ( MenuSelect ( event.where ) );
  95.                         break;
  96.                     case inSysWindow:
  97.                         SystemClick ( &event, whichWindow );
  98.                         break;
  99.                     case inDrag:
  100.                         if ( whichWindow == TheWindow ) DragWindow ( TheWindow, event.where, &DragRect );
  101.                         break;
  102.                     case inContent:
  103.                         if ( whichWindow == TheWindow ) {
  104.                             if ( TheWindow != FrontWindow ( ) ) SelectWindow ( TheWindow );
  105.                             else DoMouseDown ( event.where,  event.modifiers );
  106.                             }
  107.                         break;
  108.                     }
  109.                 break;
  110.             case autoKey:
  111.             case keyDown:
  112.                 key = (char) (event.message & charCodeMask);
  113.                 if ( event.modifiers & cmdKey ) DoMenu ( MenuKey ( key ) );
  114.                 else if ( key == RETURNKEY || key == ENTERKEY ) SendMessage ( false );
  115.                 else {
  116.                     ForeColor ( greenColor );
  117.                     TEKey ( key, MyMessage );
  118.                     if ( (*MyMessage)->teLength > MAXMSG ) {
  119.                         TESetSelect ( (long) MAXMSG, MAXTEXT, MyMessage );
  120.                         TEDelete ( MyMessage );
  121.                         }
  122.                     ForeColor ( blackColor );
  123.                     }
  124.                 break;
  125.             case nullEvent:
  126.                 if ( MySocketID == 0 ) StartAT ( );
  127.                 else {
  128.                     ShowStatus ( WAITING );
  129.                     if ( (*InABRecord)->abResult <= 0 ) ReadMessage ( );
  130.                     TEIdle ( MyMessage );
  131.                     }
  132.                 break;
  133.             }
  134.         }
  135.  
  136.     ByeBye ( );
  137.     }
  138.  
  139. /*|||||||||||||||||||| initialize the Mac and my globals */
  140. Initialize ()
  141.     {
  142.     EventRecord        event;
  143.     Handle                h;
  144.     
  145.     /*** init the mac rom stuff */
  146.     InitGraf ( &thePort );
  147.     InitFonts ();
  148.     InitWindows ();
  149.     InitMenus ();
  150.     TEInit ();
  151.     InitDialogs ( 0L );
  152.     InitCursor ();
  153.     FlushEvents ( everyEvent, 0 );
  154.     
  155.     /*** set up a few globals ... */
  156.     EchoFlag = BeepFlag = SelectFlag = true;
  157.     DoneFlag = false;
  158.     MyNameBuf = 0L;
  159.     InABRecord = (DDPProto **) NewHandle ( sizeof ( DDPProto ) );
  160.     MoveHHi ( InABRecord );
  161.     HLock ( InABRecord );
  162.     DragRect = screenBits.bounds;
  163.     MySocketID = 0;
  164.     NumOtherUsers = 0;
  165.     
  166.     /*** read in the menus */
  167.     AppleMenu = GetMenu ( 1000 );
  168.     InsertMenu ( AppleMenu, 0 );
  169.     AddResMenu ( AppleMenu, 'DRVR' );
  170.     FileMenu = GetMenu ( 1001 );
  171.     InsertMenu ( FileMenu, 0 );
  172.     EditMenu = GetMenu ( 1002 );
  173.     InsertMenu ( EditMenu, 0 );
  174.     OptionMenu = GetMenu ( 1003 );
  175.     InsertMenu ( OptionMenu, 0 );
  176.     DrawMenuBar ();
  177.  
  178.     /*** make sure appletalk is available */
  179.     if ( (TheError = MPPOpen ()) != noErr ) 
  180.         ErrorAlert ( "\PCan't start AppleTalk, error:", TheError );
  181.  
  182.     /*** get user's name */
  183.     h = (Handle) GetString ( -16096 );
  184.     if ( h != NULL ) BlockMove ( *h, MyName, ((long) **h) + 1L );
  185.     else MyName[0] = 0;
  186.     ReleaseResource ( h );
  187.     NameDlog ( );
  188.     
  189.     /*** open our window & init */
  190.     InitTheWindow ( );
  191.     }
  192.  
  193. /*|||||||||||||||||||| process menu picks */
  194. DoMenu ( mResult )
  195.     long    mResult;
  196.     {
  197.     int            theMenu, theItem, i;
  198.     Str255        daname;
  199.     DialogPtr    aboutdlog;
  200.     GrafPtr        curport;
  201.     TEHandle    te;
  202.     
  203.     theItem = LoWord ( mResult );
  204.     theMenu = HiWord ( mResult );
  205.     switch ( theMenu ) {
  206.         case 1000:
  207.             if ( theItem == 1 ) {
  208.                 aboutdlog = GetNewDialog ( 2000, NULL, -1L );
  209.                 i = 0;
  210.                 while ( !i ) ModalDialog ( NULL, &i );
  211.                 DisposDialog ( aboutdlog );
  212.                 }
  213.             else {
  214.                 GetItem ( AppleMenu, theItem, daname );
  215.                 GetPort ( &curport );
  216.                 OpenDeskAcc ( daname );
  217.                 SetPort ( curport );
  218.                 }
  219.             break;
  220.         case 1001:
  221.             if ( theItem == 1 ) DoneFlag = true;
  222.             break;
  223.         case 1002:
  224.             ForeColor ( greenColor );
  225.             switch ( theItem ) {
  226.                 case 3:
  227.                     TECut ( MyMessage );            
  228.                     break;
  229.                 case 4:
  230.                     TECopy ( MyMessage );
  231.                     break;
  232.                 case 5:                    
  233.                     TEPaste ( MyMessage );
  234.                     if ( (*MyMessage)->teLength > MAXMSG ) {
  235.                         TESetSelect ( (long) MAXMSG, MAXTEXT, MyMessage );
  236.                         TEDelete ( MyMessage );
  237.                         }
  238.                     break;
  239.                 }
  240.             ForeColor ( blackColor );
  241.             break;
  242.         case 1003:
  243.             switch ( theItem ) {
  244.                 case 1:
  245.                     if ( BeepFlag ) {        
  246.                         BeepFlag = false;        
  247.                         CheckItem ( OptionMenu, 1, false );
  248.                         }
  249.                     else {
  250.                         BeepFlag = true;
  251.                         CheckItem ( OptionMenu, 1, true );
  252.                         }
  253.                     break;
  254.                 case 2:
  255.                     if ( EchoFlag ) {
  256.                         EchoFlag = false;
  257.                         CheckItem ( OptionMenu, 2, false );
  258.                         }
  259.                     else {
  260.                         EchoFlag = true;
  261.                         CheckItem ( OptionMenu, 2, true );
  262.                         }
  263.                     break;
  264.                 case 3:
  265.                     if ( SelectFlag ) {
  266.                         SelectFlag = false;
  267.                         CheckItem ( OptionMenu, 3, false );
  268.                         }
  269.                     else {
  270.                         SelectFlag = true;
  271.                         CheckItem ( OptionMenu, 3, true );
  272.                         }
  273.                     break;
  274.                 }
  275.             break;
  276.         }
  277.  
  278.     HiliteMenu(0);
  279.     }
  280.  
  281. /*|||||||||||||||||||| get the name the user wants to go by */
  282. NameDlog ( )
  283.     {
  284.     DialogPtr        dlog;
  285.     int                itype, item;
  286.     Handle            h, namefield;
  287.     Rect                r;
  288.     Str255            astr;
  289.  
  290.     /*** do the dialog */
  291.     dlog = GetNewDialog ( 1000, 0L, -1L );
  292.     GetDItem ( dlog, 4, &itype, &namefield, &r );
  293.     SetIText ( namefield, MyName );
  294.     SelIText ( dlog, 4, 0, 255 );
  295.     item = 0;
  296.     while ( item != 1 ) ModalDialog ( 0L, &item );
  297.  
  298.     /*** save off the name, 32 chars max */
  299.     GetIText ( namefield, &MyName );
  300.     if ( ((int) *MyName) > 31 ) *MyName = (char) 31;
  301.     
  302.     CloseDialog ( dlog );
  303.     }
  304.  
  305. /*|||||||||||||||||||| initialize AppleTalk stuff */
  306. StartAT ( )
  307.     {
  308.     /*** open a socket, use defalut listener */
  309.     if ( (TheError = DDPOpenSocket ( &MySocketID, NULL )) != noErr ) 
  310.         ErrorAlert (    "\PDDPOpenSocket ( )  returned:", TheError );
  311.     
  312.     /*** register and check for other users, greet everyone */
  313.     RegisterName ( );
  314.     CheckForUsers ( );
  315.     SendGreeting ( );
  316.     
  317.     /*** listen for messages */
  318.     AskForMessage ( );
  319.     }
  320.  
  321. /*|||||||||||||||||||| Register MyName as a "Chatterbox"  via AppleTalk NBP */
  322. RegisterName ( )
  323.     {
  324.     int                len;
  325.     NBPProto        **myABRecNBP;
  326.     
  327.     /*** show some feedback */
  328.     ShowStatus ( REGISTER );
  329.  
  330.     /*** make up a handle for registering */
  331.     myABRecNBP = (NBPProto **) NewHandle ( sizeof ( NBPProto ) );
  332.     HLock ( myABRecNBP );
  333.  
  334.     /*** set up our entity name */
  335.     BlockMove ( MyName, MyEntityName.objStr, ((long) *MyName) + 1L );
  336.     BlockMove ( "\PChatterbox", MyEntityName.typeStr, 11L );
  337.     BlockMove ( "\P*", MyEntityName.zoneStr, 2L );
  338.     
  339.     /*** set up a buffer for NBP's internal use */
  340.     len = ((int) *MyName) + 26;
  341.     MyNameBuf = NewPtr ( (long) len );
  342.     
  343.     /*** fill in the record & register */
  344.     (*myABRecNBP)->abOpcode = tNBPRegister;
  345.     (*myABRecNBP)->abUserReference = 0L;
  346.     (*myABRecNBP)->nbpEntityPtr = &MyEntityName;
  347.     (*myABRecNBP)->nbpBufPtr = MyNameBuf;
  348.     (*myABRecNBP)->nbpBufSize = len;
  349.     (*myABRecNBP)->nbpAddress.aSocket = (Byte) MySocketID;
  350.     (*myABRecNBP)->nbpRetransmitInfo.retransInterval = 8;
  351.     (*myABRecNBP)->nbpRetransmitInfo.retransCount = 2;
  352.     if ( (TheError = NBPRegister ( myABRecNBP, false )) != noErr ) 
  353.         ErrorAlert ( "\PNBPRegister ( )  returned:", TheError );
  354.     
  355.     HUnlock ( myABRecNBP );
  356.     DisposHandle ( myABRecNBP );
  357.     }
  358.  
  359. /*|||||||||||||||||||| remove the name from NBP */
  360. UnRegisterName ( )
  361.     {
  362.     if ( (TheError = NBPRemove ( &MyEntityName )) != noErr ) 
  363.         ErrorAlert ( "\PNBPRemove ( )  returned:", TheError );
  364.     DisposPtr ( MyNameBuf );
  365.     }
  366.  
  367. /*|||||||||||||||||||| check for other users on the net. */
  368. CheckForUsers ( )
  369.     {
  370.     int                j, r, i, numFound, found, removed;
  371.     Point                c;
  372.     EntityName    otherEntity;
  373.     NBPProto        **otherABRecNBP;
  374.     AddrBlock        addblock;
  375.     
  376.     /*** show some feedback */
  377.     ShowStatus ( SCANNING );
  378.  
  379.     /*** mark them all as "not found" */
  380.     for ( i = 0; i < NumOtherUsers; ++i ) OtherUsers[i].found = false;
  381.  
  382.     /*** now scan for other users */
  383.     BlockMove ( "\P=", otherEntity.objStr, 2L );
  384.     BlockMove ( "\PChatterbox", otherEntity.typeStr, 11L );
  385.     BlockMove ( "\P*", otherEntity.zoneStr, 2L );
  386.     otherABRecNBP = (NBPProto **) NewHandle ( sizeof ( NBPProto ) );
  387.     HLock ( otherABRecNBP );
  388.     (*otherABRecNBP)->abOpcode = tNBPLookup;
  389.     (*otherABRecNBP)->abUserReference = 0L;
  390.     (*otherABRecNBP)->nbpEntityPtr = &otherEntity;
  391.     (*otherABRecNBP)->nbpBufPtr = NewPtr ( 4096 );
  392.     (*otherABRecNBP)->nbpBufSize = 4096;
  393.     (*otherABRecNBP)->nbpDataField = 40;
  394.     (*otherABRecNBP)->nbpRetransmitInfo.retransInterval = 8;
  395.     (*otherABRecNBP)->nbpRetransmitInfo.retransCount = 1;
  396.     if ( (TheError = NBPLookup ( otherABRecNBP, false )) != noErr ) 
  397.         ErrorAlert ( "\PNBPLookup ( )  returned:", TheError );
  398.     
  399.     /*** if we found any, check for them in the list, else empty the list */
  400.     numFound = (*otherABRecNBP)->nbpDataField;
  401.     if ( numFound <= 0 ) {
  402.         j = (*UserList)->dataBounds.bottom;
  403.         if ( j > 0 ) LDelRow ( j, 0, UserList );
  404.         NumOtherUsers = 0;
  405.         }
  406.     else {
  407.         for ( i = 0; i < numFound; ++i ) {
  408.         
  409.             /*** extract it */
  410.             if ( (TheError = NBPExtract ( (*otherABRecNBP)->nbpBufPtr, 
  411.                     numFound, i+1, &otherEntity, &addblock )) == noErr ) {
  412.                 
  413.                 /*** see if it's in the list */
  414.                 found = false;
  415.                 for ( j = 0; j < NumOtherUsers; ++j ) {
  416.                     if ( OtherUsers[j].addr.aSocket == addblock.aSocket &&
  417.                             OtherUsers[j].addr.aNode == addblock.aNode &&
  418.                             OtherUsers[j].addr.aNet == addblock.aNet ) { found = true; break; }
  419.                     }
  420.  
  421.                 /*** if it is, mark it */
  422.                 if ( found ) OtherUsers[j].found = true;
  423.                 
  424.                 /*** else add it, select it if auto-select is on */
  425.                 else {
  426.                     c.h = 0;
  427.                     c.v = NumOtherUsers;
  428.                     OtherUsers[NumOtherUsers].addr = addblock;
  429.                     OtherUsers[NumOtherUsers].found = true;
  430.                     LAddRow ( 1, NumOtherUsers, UserList );
  431.                     LSetCell ( &otherEntity.objStr[1], (int) otherEntity.objStr[0], c, UserList );
  432.                     if ( SelectFlag ) LSetSelect ( true, c, UserList );
  433.                     ++NumOtherUsers;
  434.                     }
  435.                 
  436.                 /*** don't overflow */
  437.                 if ( NumOtherUsers >= MAXUSERS ) break;
  438.                 }
  439.             }
  440.  
  441.         /*** delete any that were not found (they've logged off ) */
  442.         for ( j = NumOtherUsers-1; j >= 0; --j ) {
  443.             if ( !OtherUsers[j].found ) {
  444.                 --NumOtherUsers;
  445.                 BlockMove ( &OtherUsers[j+1], &OtherUsers[j], (long) ((NumOtherUsers-j)*sizeof(UserRecord)) );
  446.                 LDelRow ( 1, j, UserList );
  447.                 }
  448.             }
  449.         }
  450.  
  451.     DisposPtr ( (*otherABRecNBP)->nbpBufPtr );
  452.     HUnlock ( otherABRecNBP );
  453.     DisposHandle ( otherABRecNBP );
  454.     }
  455.  
  456. /*|||||||||||||||||||| tell DDP we're waiting for a msg */
  457. AskForMessage ( )
  458.     {
  459.     (*InABRecord)->abOpcode = tDDPRead;
  460.     (*InABRecord)->abResult = 0;
  461.     (*InABRecord)->abUserReference = 0L;
  462.     (*InABRecord)->ddpType = 5;
  463.     (*InABRecord)->ddpSocket = MySocketID;
  464.     (*InABRecord)->ddpReqCount = BUFSIZE;
  465.     (*InABRecord)->ddpDataPtr = RecBuffer;
  466.     if ( (TheError = DDPRead ( InABRecord, true, true )) != noErr )
  467.         ErrorAlert ( "\PDDPRead ( )  returned:", TheError );
  468.     }
  469.  
  470. /*|||||||||||||||||||| read a message from the socket */
  471. ReadMessage ( )
  472.     {
  473.     Rect            r;
  474.     Cell            c;
  475.     Str255        othername;
  476.     int            i, namelen, nlines, fromnode;
  477.     
  478.     /*** make sure there's really a message */
  479.     if ( (*InABRecord)->ddpActCount > 0 ) {
  480.     
  481.         /*** show some feedback */
  482.         ShowStatus ( RECEIVING );
  483.         
  484.         /*** check for users if it's a special message (ie, has option-space) */
  485.         if ( RecBuffer[0] == ' ' ) CheckForUsers ();
  486.  
  487.         /*** see who it's from */
  488.         namelen = 0;
  489.         fromnode = (*InABRecord)->ddpAddress.aNode;
  490.         for ( i = 0; i < NumOtherUsers; ++i ) if ( fromnode == OtherUsers[i].addr.aNode ) break;
  491.         if ( i < NumOtherUsers ) {
  492.             c.h = 0;
  493.             c.v = i;
  494.             namelen = 32;
  495.             LGetCell ( othername, &namelen, c, UserList );
  496.             }
  497.  
  498.         /*** put the message in the display */
  499.         ForeColor ( blueColor );
  500.         TESetSelect ( MAXTEXT, MAXTEXT, InMessages );
  501.         TEInsert ( RecBuffer, (long) (*InABRecord)->ddpActCount, InMessages );
  502.         TEInsert ( " (", 2L, InMessages );
  503.         TEInsert ( othername, (long) namelen, InMessages );
  504.         TEInsert ( ")\015", 2L, InMessages );
  505.         nlines = (*InMessages)->nLines;
  506.         for ( i = DISPLINES; i < nlines; ++i ) DeleteFirstLine ( InMessages );
  507.         ForeColor ( blackColor );
  508.         
  509.         if ( BeepFlag ) SysBeep ( 1 );
  510.         }
  511.     else ErrorAlert ( "\PError in recieved packet.", 0 );
  512.     
  513.     AskForMessage ( );
  514.     }
  515.  
  516. /*|||||||||||||||||||| send the message out.  If toAll is true, send to all, else send to selected. */
  517. SendMessage ( toAll )
  518.     int                toAll;
  519.     {
  520.     DDPProto        **outABRecord;
  521.     Handle            thetext;
  522.     int                i, nlines, textlen, msgSent;
  523.     Cell                c;
  524.     char                xmitBuffer[BUFSIZE];    
  525.     
  526.     /*** only if there is something to say */
  527.     if ( (*MyMessage)->teLength <= 0 ) return;
  528.     
  529.     /*** get a record */
  530.     outABRecord = (DDPProto **) NewHandle ( sizeof ( DDPProto ) );
  531.     HLock ( outABRecord );
  532.  
  533.     /*** copy the text to the out buffer, empty TextEdit record */
  534.     thetext = (Handle) TEGetText ( MyMessage );
  535.     HLock ( thetext );
  536.     textlen = (*MyMessage)->teLength;
  537.     BlockMove ( *thetext, xmitBuffer, textlen );
  538.     HUnlock ( thetext );
  539.     
  540.     /*** send the message to appropriate users */
  541.     for ( msgSent = false, i = 0; i <NumOtherUsers; ++i ) {
  542.         c.h = 0;
  543.         c.v = i;
  544.         if ( toAll || LGetSelect ( false, &c, UserList ) ) {
  545.             ShowStatus ( SENDING );
  546.             (*outABRecord)->abOpcode = tDDPWrite;
  547.             (*outABRecord)->abUserReference = 0L;
  548.             (*outABRecord)->ddpType = 5;
  549.             (*outABRecord)->ddpSocket = MySocketID;
  550.             (*outABRecord)->ddpAddress = OtherUsers[i].addr;
  551.             (*outABRecord)->ddpDataPtr = xmitBuffer;
  552.             (*outABRecord)->ddpReqCount = textlen;
  553.             if ( (TheError = DDPWrite ( outABRecord, false, false )) != noErr )
  554.                 ErrorAlert ( "\PDDPWrite ( )  returned:", TheError );
  555.             msgSent = true;
  556.             }
  557.         }
  558.  
  559.     if ( msgSent ) {
  560.         TESetSelect ( 0L, MAXTEXT, MyMessage );
  561.         TEDelete ( MyMessage );
  562.         TESetSelect ( 0L, 0L, MyMessage );
  563.         
  564.         if ( EchoFlag ) {
  565.             ForeColor ( blueColor );
  566.             TESetSelect ( MAXTEXT, MAXTEXT, InMessages );
  567.             TEInsert ( "«", 1L, InMessages );
  568.             TEInsert ( xmitBuffer, (long) textlen, InMessages );
  569.             TEInsert ( "»\015", 2L, InMessages );
  570.             nlines = (*InMessages)->nLines;
  571.             for ( i = DISPLINES; i < nlines; ++i ) DeleteFirstLine ( InMessages );
  572.             ForeColor ( blackColor );
  573.             }
  574.         }
  575.  
  576.     HUnlock ( outABRecord );
  577.     DisposHandle ( outABRecord );
  578.     }
  579.  
  580. /*|||||||||||||||||||| say hello to existing users */
  581. SendGreeting ( ) 
  582.     {
  583.     Str255        greeting;
  584.     
  585.     /*** send log-on messge (has option-space) */
  586.     greeting[0] = '\0';
  587.     Pstrcat ( greeting, "\P  Hello Everyone!" );
  588.     TESetSelect ( 0L, 0L, MyMessage );
  589.     TEInsert ( &greeting[1], (long) greeting[0], MyMessage );
  590.     SendMessage ( true );
  591.     TESetSelect ( 0L, MAXTEXT, MyMessage );
  592.     TEDelete ( MyMessage );
  593.     }
  594.  
  595. /*|||||||||||||||||||| pick up our toys and go home */
  596. ByeBye ( ) 
  597.     {
  598.     Str255        adios;
  599.     
  600.     /*** send logoff message (has option-space) */
  601.     adios[0] = '\0';
  602.     Pstrcat ( adios, "\P  " );
  603.     Pstrcat ( adios, MyName );
  604.     Pstrcat ( adios, "\P is off!" );
  605.     TESetSelect ( 0L, 0L, MyMessage );
  606.     TEInsert ( &adios[1], (long) adios[0], MyMessage );
  607.     SendMessage ( true );
  608.  
  609.     /*** close everything */
  610.     UnRegisterName ( );
  611.     DDPCloseSocket ( (Byte) MySocketID );
  612.     CloseWindow ( TheWindow );
  613.     }
  614.  
  615. /*|||||||||||||||||||| delete the first line from the given TEhandle */
  616. DeleteFirstLine ( te )
  617.     TEHandle    te;
  618.     {
  619.     Handle        h;
  620.     int            charsinline = 0;
  621.     char            *cp;
  622.     
  623.     h = TEGetText ( te );
  624.     HLock ( h );
  625.     cp = *h;
  626.     while ( *cp != '\015' ) { ++cp; ++charsinline; }
  627.     ++charsinline;
  628.     TESetSelect ( 0L, (long) charsinline, te );
  629.     TEDelete ( te );
  630.     }
  631.  
  632. /*|||||||||||||||||||| open and initialize the window */
  633. InitTheWindow ( )
  634.     {
  635.     Rect                    r, dbr, view, dest;
  636.     Point                    csize;
  637.     char                    title[80];
  638.     
  639.     TheWindow = GetNewWindow ( 1000, 0L, -1L );
  640.     title[0] = '\0';
  641.     Pstrcat ( title, "\PChatterbox - " );
  642.     Pstrcat ( title, MyName );
  643.     SetWTitle ( TheWindow, title );
  644.     SetPort ( TheWindow );
  645.     TextFont ( 4 );
  646.      TextSize ( 9 );
  647.      TextFace ( 0 );
  648.  
  649.     /*** add in the controls */
  650.     ScanButton = GetNewControl ( 1000, TheWindow );
  651.     SelectAllButton = GetNewControl ( 2000, TheWindow );
  652.     
  653.     /*** add in the user list */
  654.     SetRect ( &r, 20, 22, 164, 94 );
  655.     SetRect ( &dbr, 0, 0, 1, 0 );
  656.     csize.v = 12;
  657.     csize.h = 300;
  658.     UserList = LNew ( &r, &dbr, csize, 0, TheWindow, true, false, false, true );
  659.     
  660.     /*** add in the outgoing message field */
  661.     SetRect ( &view, 21, 114, 339, 128 );
  662.     SetRect ( &dest, 22, 115, 338, 270 );
  663.     MyMessage = TENew ( &dest, &view );
  664.     TESetSelect ( 0L, 0L, MyMessage );
  665.     
  666.     /*** set up the incomming message area */
  667.     SetRect ( &view, 21, 148, 339, 238 );
  668.     SetRect ( &dest, 22, 149, 338, 270 );
  669.     InMessages = TENew ( &dest, &view );
  670.     TESetSelect ( 0L, 0L, InMessages );
  671.  
  672.     /*** set up the status area */
  673.     SetRect ( &StatusR, 196, 21, 340, 65 );
  674.     }
  675.  
  676. /*|||||||||||||||||||| handle update events for our main (and only) window */
  677.  DoUpdate ( )
  678.      {
  679.      Rect            r;
  680.     RgnHandle    vis;
  681.      
  682.      /*** do the controls */
  683.      SetPort ( TheWindow );
  684.      DrawControls ( TheWindow );
  685.      
  686.      /*** show the user list */
  687.      r = (*UserList)->rView;
  688.     InsetRect ( &r, -1, -1 );
  689.     FrameRect ( &r );
  690.     MoveTo ( r.left, r.top-4 );
  691.     DrawString ( "\PSelect Recipients:" );
  692.     vis = TheWindow->visRgn;
  693.     HandToHand ( &vis );
  694.     LUpdate ( vis, UserList );
  695.     DisposHandle ( vis );
  696.     
  697.     /*** show the MyMessage field */
  698.     r = (*MyMessage)->viewRect;
  699.     MoveTo ( r.left-1, r.top-6 );
  700.     DrawString ( "\PType a message, <return> to send:" );
  701.     EraseRect ( &r );
  702.     InsetRect ( &r, -2, -2 );
  703.     FrameRect ( &r );
  704.     ForeColor ( greenColor );
  705.     TEUpdate ( &r, MyMessage );
  706.     ForeColor ( blackColor );
  707.  
  708.     /*** show the incomming message field */
  709.     r = (*InMessages)->viewRect;
  710.     MoveTo ( r.left-1, r.top-6 );
  711.     DrawString ( "\PReceived Messages:" );
  712.     EraseRect ( &r );
  713.     InsetRect ( &r, -2, -2 );
  714.     FrameRect ( &r );
  715.     ForeColor ( blueColor );
  716.     TEUpdate ( &r, InMessages );
  717.     ForeColor ( blackColor );
  718.     
  719.     /*** show the status area */
  720.     MoveTo ( StatusR.left, StatusR.top-4 );
  721.     DrawString ( "\PStatus:" );
  722.     FrameRect ( &StatusR );
  723.     ShowStatus ( NONE );
  724.      }
  725.  
  726. /*|||||||||||||||||||| display the status notice */
  727. ShowStatus ( status )
  728.     int        status;
  729.     {
  730.     Rect            r;
  731.     char            *cp;
  732.     static int    oldstatus;    
  733.     
  734.     if ( status != oldstatus ) {
  735.         InsetRect ( &StatusR, 2, 2 );
  736.         EraseRect ( &StatusR );
  737.         ForeColor ( redColor );
  738.         cp = NULL;
  739.         switch ( status ) {
  740.             case NONE:                    break;
  741.             case REGISTER:            cp = "\PRegistering on the network.";    break;
  742.             case WAITING:                cp = "\PListening…";                            break;
  743.             case SCANNING:            cp = "\PScanning for other users.";        break;        
  744.             case SENDING:                cp = "\PSending message.";                    break;        
  745.             case RECEIVING:            cp = "\PReceiving message.";                break;        
  746.             }
  747.         if ( cp != NULL ) TextBox ( (cp + 1), (long) *cp, &StatusR, teJustLeft );
  748.         ForeColor ( blackColor );
  749.         InsetRect ( &StatusR, -2, -2 );
  750.         oldstatus = status;
  751.         }
  752.     }
  753.  
  754. /*|||||||||||||||||||| handle mouse-downs in our window */
  755. DoMouseDown ( thePt, mods )
  756.     Point            thePt;
  757.     int            mods;
  758.     {
  759.     Rect                    r;
  760.     Point                    localPt, c;
  761.     ControlHandle        chan;
  762.     
  763.     /*** get the local point */
  764.     localPt = thePt;
  765.     GlobalToLocal ( &localPt );
  766.     
  767.     /*** in UserList or it's scroll bars? */
  768.     r = (*UserList)->rView;
  769.     r.right += 16;
  770.     if ( PtInRect ( localPt, &r ) && ( NumOtherUsers > 0 ) ) {
  771.         LClick ( localPt, mods, UserList );
  772.         return;
  773.         }
  774.  
  775.     /*** in MyMessage ? */
  776.     r = (*MyMessage)->viewRect;
  777.     if ( PtInRect ( localPt, &r ) ) {
  778.         TEClick ( localPt, ((mods & shiftKey) == shiftKey), MyMessage );
  779.         return;
  780.         }
  781.     
  782.     /*** in the controls */
  783.     if ( FindControl ( localPt, TheWindow, &chan ) ) {
  784.         if ( TrackControl ( chan, localPt, NULL ) ) {
  785.             if ( chan == ScanButton ) CheckForUsers ( );
  786.             if ( chan == SelectAllButton ) {
  787.                 for ( c.h = 0, c.v = (*UserList)->dataBounds.top; c.v < (*UserList)->dataBounds.bottom; ++c.v )
  788.                     LSetSelect ( true, c, UserList );
  789.                 }
  790.             }
  791.         }
  792.     }
  793.  
  794. /*|||||||||||||||||||| catenate the second pascal string to the first */
  795. Pstrcat ( str1, str2 )
  796.     char *str1, *str2;
  797.     {
  798.     char    *str1len, *str1end;
  799.     
  800.     str1len = str1;
  801.     str1end = str1 + (int) *str1len + 1;
  802.     BlockMove ( (str2 + 1), str1end, (long) *str2 );
  803.     *str1len += (int) *str2;
  804.     }
  805.  
  806. /*|||||||||||||||||||| alert the user to an error, optionally exit to shell */
  807. ErrorAlert ( where, errorNum )
  808.     char    *where;
  809.     int    errorNum;
  810.     {
  811.     Str255        idstr;
  812.  
  813.     /*** give the alert */
  814.     NumToString ( (long) errorNum, idstr );
  815.     ParamText ( where, idstr, "\P", "\P" );
  816.     
  817.     if ( Alert ( 666, 0L ) == 1 ) {
  818.         UnRegisterName ( );
  819.         DDPCloseSocket ( (Byte) MySocketID );
  820.         ExitToShell ();
  821.         }
  822.     }
  823.